package com.wgke.bluetooth.utils;

import android.util.Log;

import com.wgke.bluetooth.tools.BtMsg;

public class ConfigurableByteArrayProcessor {
    private byte[] header = {(byte) 0x5A, (byte) 0xA5};
    private byte footer = (byte) 0xAA;
    private boolean useLengthField = true;
    private boolean useChecksum = true;

    private byte[] buffer = new byte[0];
    private OnPacketReceivedListener listener;

    private static final String TAG = "ConfigurableByteArrayProcessor";

    public interface OnPacketReceivedListener {
        void onPacketReceived(byte[] packet);
    }

    public static class Builder {
        private byte[] header = {(byte) 0x5A, (byte) 0xA5};
        private byte footer = (byte) 0xAA;
        private boolean useLengthField = true;
        private boolean useChecksum = true;

        public Builder setHeader(byte first, byte second) {
            this.header = new byte[]{first, second};
            return this;
        }

        public Builder setFooter(byte footer) {
            this.footer = footer;
            return this;
        }

        public Builder setUseLengthField(boolean use) {
            this.useLengthField = use;
            return this;
        }

        public Builder setUseChecksum(boolean use) {
            this.useChecksum = use;
            return this;
        }

        public ConfigurableByteArrayProcessor build(OnPacketReceivedListener listener) {
            return new ConfigurableByteArrayProcessor(this, listener);
        }
    }

    private ConfigurableByteArrayProcessor(Builder builder, OnPacketReceivedListener listener) {
        this.header = builder.header;
        this.footer = builder.footer;
        this.useLengthField = builder.useLengthField;
        this.useChecksum = builder.useChecksum;
        this.listener = listener;
    }

    public boolean process(byte[] newData) {
        byte[] newBuffer = new byte[buffer.length + newData.length];
        System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
        System.arraycopy(newData, 0, newBuffer, buffer.length, newData.length);
        buffer = newBuffer;
        boolean result=false;

        while (true) {
            PacketInfo packetInfo = tryFindPacket();
            if (packetInfo == null || packetInfo.packetLength == -1) {
                break; // No complete packet found
            }
            Log.i("ConfigurableByteArrayProcessor", "packetInfo: " + packetInfo.packetLength + "/" + packetInfo.startIndex);
            // Extract the complete packet
            byte[] packet = new byte[packetInfo.packetLength];
            System.arraycopy(buffer, packetInfo.startIndex, packet, 0, packetInfo.packetLength);

            // Notify listener
            if (listener != null) {
                listener.onPacketReceived(packet);
                result=true;
            }

            // Remove processed data from buffer
            int remainingLength = buffer.length - (packetInfo.startIndex + packetInfo.packetLength);
            byte[] remainingBuffer = new byte[remainingLength];
            System.arraycopy(buffer, packetInfo.startIndex + packetInfo.packetLength,
                    remainingBuffer, 0, remainingLength);
            buffer = remainingBuffer;
        }
        return result;//单条输入，是否满足接口
    }

    private static class PacketInfo {
        int startIndex;
        int packetLength;

        PacketInfo(int startIndex, int packetLength) {
            this.startIndex = startIndex;
            this.packetLength = packetLength;
        }
    }

    private PacketInfo tryFindPacket() {
        // Minimum packet length: header + length byte + min body + checksum + footer
        int minPacketLength = header.length + (useLengthField ? 1 : 0) +
                (useChecksum ? 2 : 0) + 1;

        Log.i(TAG, "tryFindPacket: buffer.length=" + buffer.length + ", minPacketLength=" + minPacketLength);

        if (buffer.length < minPacketLength) {
            return null;
        }

        // Find header
        int startIndex = findHeader();
        Log.i(TAG, "tryFindPacket: header found at " + startIndex);
        if (startIndex == -1) {
            // No header found, discard all data
            buffer = new byte[0];
            return null;
        }

        // Calculate expected packet length
        int lengthFieldPos = startIndex + header.length;
        Log.i(TAG, "tryFindPacket: lengthFieldPos=" + lengthFieldPos);
        if (lengthFieldPos >= buffer.length) {
            return null;
        }

        // Get body length from first byte after header
        int bodyLength = buffer[lengthFieldPos] & 0xFF;

        // Calculate full packet length
        int fullPacketLength = header.length + bodyLength +
                (useChecksum ? 2 : 0) + 1;

        Log.i(TAG, "tryFindPacket: bodyLength=" + bodyLength + ", fullPacketLength=" + fullPacketLength);
        // Check if we have enough data
        if (buffer.length - startIndex < fullPacketLength) {
            return null;
        }

        // Verify footer
        int footerPos = startIndex + fullPacketLength - 1;
        Log.i(TAG, "tryFindPacket: footerPos=" + footerPos);
        if (buffer[footerPos] != footer) {
            // Footer mismatch, skip this header and continue searching
            byte[] newBuffer = new byte[buffer.length - startIndex - header.length];
            System.arraycopy(buffer, startIndex + header.length, newBuffer, 0, newBuffer.length);
            buffer = newBuffer;
            return tryFindPacket();
        }

        // Verify checksum if enabled
        if (useChecksum) {
            int checksumStart = lengthFieldPos;
            int checksumEnd = checksumStart + bodyLength;
            int checksumPos = checksumEnd;

            if (checksumPos + 1 >= buffer.length) {
                return null;
            }

            int calculatedSum = 0;
            for (int i = lengthFieldPos; i < checksumEnd; i++) {
                calculatedSum += buffer[i] & 0xFF;
            }

            int receivedSum = ((buffer[checksumPos] & 0xFF) << 8) | (buffer[checksumPos + 1] & 0xFF);

            Log.i(TAG, "tryFindPacket: calculatedSum=" + calculatedSum + ", receivedSum=" + receivedSum);
            if (calculatedSum != receivedSum) {
                // Checksum failed, skip this header
                byte[] newBuffer = new byte[buffer.length - startIndex - header.length];
                System.arraycopy(buffer, startIndex + header.length, newBuffer, 0, newBuffer.length);
                buffer = newBuffer;
                return tryFindPacket();
            }
        }

        return new PacketInfo(startIndex, fullPacketLength);
    }

    private int findHeader() {
        for (int i = 0; i <= buffer.length - header.length; i++) {
            boolean match = true;
            for (int j = 0; j < header.length; j++) {
                if (buffer[i + j] != header[j]) {
                    match = false;
                    break;
                }
            }
            if (match) {
                return i;
            }
        }
        return -1;
    }
}